package com.zmn.plat.services.impl.area;

import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.zmn.common.utils.date.DateUtil;
import com.zmn.plat.common.dto.KVDTO;
import com.zmn.plat.model.vo.area.AreaSimpleVO;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dictionary.DictModel;
import com.zmn.common.dictionary.GlobalDict;
import com.zmn.common.dictionary.KvModel;
import com.zmn.common.dto.AreaDTO;
import com.zmn.common.dto.VtDTO;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.InitialUtil;
import com.zmn.manager.redis.RedisManager;
import com.zmn.mcc.common.constant.DpermitConsts;
import com.zmn.mcc.common.dto.permit.SpermitDTO;
import com.zmn.mcc.dubbo.dto.DubboConsts;
import com.zmn.mcc.dubbo.interfaces.staff.StaffPermitRemoteService;
import com.zmn.mcc.permit.mysql.annotation.ZmnDPermit;
import com.zmn.mcc.permit.mysql.enums.DPermitTypeEnum;
import com.zmn.plat.business.interfaces.area.BaseAreaBService;
import com.zmn.plat.common.constant.BaseAreaConsts;
import com.zmn.plat.common.dictionary.PlatErrorCodeDict;
import com.zmn.plat.common.exception.PlatException;
import com.zmn.plat.manager.interfaces.cache.AreaCache;
import com.zmn.plat.model.entity.area.AreaQuery;
import com.zmn.plat.model.entity.area.BaseArea;
import com.zmn.plat.model.entity.area.CascadeAreaVO;
import com.zmn.plat.persistence.interfaces.area.AreaDao;
import com.zmn.plat.services.interfaces.area.BaseAreaService;
import com.zmn.tapi.dubbo.interfaces.baidu.BaiduMapRemoteService;
import com.zmn.tapi.common.baidu.AddressComponent;

import lombok.extern.slf4j.Slf4j;

/**
 * @author nowind
 * @modifier tanbiao 20190909 新增状态更新和排序更新的接口方法
 * @since 17/10/30 11:30
 */
@Slf4j
@Service
public class BaseAreaServiceImpl implements BaseAreaService {
    /**
     * 直辖市 areaId
     */
    protected static final Collection<Integer> MUNICIPALITY_AREA_IDS = Arrays.asList(110000, 120000, 310000, 500000);

    @Resource
    private AreaDao areaDao;
    @Resource
    private AreaCache areaCache;

    @Resource
    BaseAreaBService baseAreaBService;
    @Resource
    RedisManager redisManager;
    @Reference(version = DubboConsts.INTERFACE_VERSION, check = false)
    StaffPermitRemoteService staffPermitRemoteService;
    @Reference(version = DubboConsts.INTERFACE_VERSION, check = false)
    BaiduMapRemoteService baiduMapRemoteService;

    private final String REDIS_AREA_LOCATION = "plat:area:location:";
    private final int REDIS_AREA_LOCATION_TIME = 60 * 60;

    @Override
    public BaseArea findCityAndProvinceByCityId(Integer cityId) throws PlatException {
        if (NumberUtil.isNullOrZero(cityId)) {
            log.error("城市ID不能为空");
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_PANAM_NULL);
        }

        BaseArea baseArea = areaDao.findCityAndProvinceByCityId(cityId);

        if (Objects.isNull(baseArea)) {
            log.error("cityId：{}对应的区域不存在,errorCode={}", cityId, PlatErrorCodeDict.ERROR_CODE_AREA_NOT_EXIST);
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_AREA_NOT_EXIST);
        }

        return baseArea;
    }

    @Override
    public BaseArea findByKey(Integer areaId) {
        return areaDao.findByKey(areaId);
    }

    @Override
    @Cacheable(key = "'service:area:key:' + #p0", cacheNames = "redis1d")
    public BaseArea findByKeyWithCache(Integer areaId) {
        return areaDao.findByKey(areaId);
    }

    @Override
    public List<BaseArea> findByKeys(List<Integer> areaIds) {
        return areaDao.findByKeys(areaIds);
    }

    @Override
    public BaseArea getAreaByLatLng(Double longitude, Double latitude) {

        AddressComponent addressComponent = null;

        try {
            addressComponent = baiduMapRemoteService.reverseGeocoding(latitude + "", longitude + "").getData().getAddressComponent();
        } catch (Exception e) {
            log.error("{} getAreaByLatLng: Tapi 调用异常", "[zmn]", e);
            e.printStackTrace();
            return null;
        }

        String key = REDIS_AREA_LOCATION + addressComponent.getAdcode();

        String redisArea = redisManager.get(key);
        if (!StringUtils.isEmpty(redisArea)) {
            BaseArea area = JSONObject.parseObject(redisArea, BaseArea.class);
            return area;
        }

        BaseArea area = getBaseAreaByLocation(addressComponent);

        if (area == null) {
            log.error("{} getAreaByLatLng，area：null, longitude: {}, latitude: {}, addressComponent: {}", longitude, latitude, addressComponent);
            return null;
        }

        area = setAreaParent(area);

        String json = JSONObject.toJSONString(area);

        redisManager.setex(key, json, REDIS_AREA_LOCATION_TIME);

        return area;
    }

    private BaseArea setAreaParent(BaseArea area) {
        int parentId = area.getParentId();

        // 如果当前是街道：5级 -> 4级
        if (area.getLevel() >= GlobalConsts.AREA_STREET2) {
            BaseArea county = areaCache.getArea(area.getParentId());
            area.setCountyId(county.getAreaId());
            area.setCountyName(county.getName());
            area.setParentId(county.getParentId());
        }

        // 如果当前是区/县：4级 -> 3级
        if (area.getLevel() >= GlobalConsts.AREA_COUNTY) {
            BaseArea city = areaCache.getArea(area.getParentId());
            area.setCityId(city.getAreaId());
            area.setCityName(city.getName());
            area.setParentId(city.getParentId());
            area.setSimpleSpell(city.getSimpleSpell());
        }

        // 如果当前是城市：3级 -> 2级
        if (area.getLevel() >= GlobalConsts.AREA_CITY) {
            BaseArea province = areaCache.getArea(area.getParentId());
            area.setProvinceId(province.getAreaId());
            area.setProvinceName(province.getName());
        }

        // 补充当前层级
        switch (area.getLevel()) {
            case GlobalConsts.AREA_STREET2:
                area.setStreetId(area.getAreaId());
                area.setStreetName(area.getName());
                break;
            case GlobalConsts.AREA_COUNTY:
                area.setCountyId(area.getAreaId());
                area.setCountyName(area.getName());
                break;
            case GlobalConsts.AREA_CITY:
                area.setCityId(area.getAreaId());
                area.setCityName(area.getName());
                break;
            case GlobalConsts.AREA_PROVINCE:
                area.setProvinceId(area.getAreaId());
                area.setProvinceName(area.getName());
                break;
        }

        area.setParentId(parentId);

        return area;
    }

    @Override
    public List<KvModel<Integer, String>> listAllCityAndPro() {
        AreaQuery query = new AreaQuery();
        query.setStatus(GlobalConsts.YES);
        query.setLevel(GlobalConsts.AREA_PROVINCE);
        //查出所有省
        List<BaseArea> baseAreaList = this.listByQuery(query);
        query = new AreaQuery();
        query.setStatus(GlobalConsts.YES);
        query.setLevel(GlobalConsts.AREA_CITY);
        //所有市
        List<BaseArea> cityList = this.listByQuery(query);
        List<KvModel<Integer, String>> kvModels = new ArrayList<>();
        BaseArea baseArea;
        for (int i = 0; i < baseAreaList.size(); i++) {
            baseArea = baseAreaList.get(i);
            KvModel model = new KvModel<>(baseArea.getAreaId(), baseArea.getName(), GlobalConsts.YES);
            List<KvModel<Integer, String>> childern = new ArrayList<>();
            for (int j = 0; j < cityList.size(); j++) {
                if (cityList.get(j).getParentId() == baseArea.getAreaId()) {
                    childern.add(new KvModel(cityList.get(j).getAreaId(), cityList.get(j).getName()));
                }
            }
            model.setChildren(childern);
            kvModels.add(model);
        }
        return kvModels;
    }

    @Override
    public List<CascadeAreaVO> getProvinceCascadeAreaVOList() {
        try {
            List<VtDTO> provinceList = baseAreaBService.getAreaList(GlobalConsts.TOP_ID);

            Collections.sort(provinceList, (vtDTO1, vtDTO2) -> vtDTO1.getValue() > vtDTO2.getValue() ? 1 : (vtDTO1.getValue() == vtDTO2.getValue() ? 0 : -1));
            List<CascadeAreaVO> provinceCascadeAreaVOList = new ArrayList<>();
            provinceList.forEach(vtDTO -> {
                CascadeAreaVO CascadeAreaVO = new CascadeAreaVO();
                CascadeAreaVO.setName(vtDTO.getText());
                CascadeAreaVO.setValue(vtDTO.getValue());
                List<CascadeAreaVO> cityCascadeAreaVOList = new ArrayList<>();
                List<VtDTO> cityList = baseAreaBService.getAreaList(vtDTO.getValue());
                cityList.forEach(cityVtDTO -> {
                    CascadeAreaVO cityCascadeAreaVO = new CascadeAreaVO();
                    cityCascadeAreaVO.setName(cityVtDTO.getText());
                    cityCascadeAreaVO.setValue(cityVtDTO.getValue());
                    cityCascadeAreaVOList.add(cityCascadeAreaVO);
                });
                CascadeAreaVO.setChildren(cityCascadeAreaVOList);
                provinceCascadeAreaVOList.add(CascadeAreaVO);
            });
            return provinceCascadeAreaVOList;
        } catch (PlatException ex) {
            throw ex;
        }
    }

    @Override
    public List<BaseArea> listPageByQuery(AreaQuery query) {
        Integer count = this.countByQuery(query);
        query.setTotalCount(count);
        return count == 0 ? new ArrayList<>(0) : areaDao.listPageByQuery(query);
    }

    @Override
    public List<VtDTO> listAllVtDto() {
        return areaDao.listAllVtDto();
    }

    @ZmnDPermit(types = {DPermitTypeEnum.PROVINCE})
    public List<BaseArea> listPageByQueryOfPermitProvince(AreaQuery query) {
        return listPageByQuery(query);
    }

    @ZmnDPermit(types = {DPermitTypeEnum.CITY})
    public List<BaseArea> listPageByQueryOfPermitCity(AreaQuery query) {
        return listPageByQuery(query);
    }

    @ZmnDPermit(types = {DPermitTypeEnum.COUNTY})
    public List<BaseArea> listPageByQueryOfPermitCounty(AreaQuery query) {
        return listPageByQuery(query);
    }

    @ZmnDPermit(types = {DPermitTypeEnum.CITY})
    public List<BaseArea> listCountyPageByQueryOfPermitCity(AreaQuery query) {
        Integer count = areaDao.countCountyPageByQueryOfPermitCity(query);
        query.setTotalCount(count);
        return count == 0 ? new ArrayList<>(0) : areaDao.listCountyPageByQueryOfPermitCity(query);
    }

    @Override
    public List<BaseArea> listByParentId(Integer parentId) {
        AreaQuery query = new AreaQuery();

        if (parentId == null || parentId < 1) {
            return null;
        }

        query.setStatus(GlobalDict.STATUS_ENABLE);
        query.setParentId(parentId);
        return this.listByQuery(query);
    }

    @Override
    public List<BaseArea> listByQuery(AreaQuery query) {
        return areaDao.listByQuery(query);
    }

    @Override
    public List<BaseArea> listAboveByLevel(Integer level, Integer status) {
        AreaQuery query = new AreaQuery();
        query.setLevel(level);
        query.setStatus(status);
        return areaDao.listAboveByLevel(query);
    }

    @Override
    public List<BaseArea> listByQueryOfPermit(AreaQuery query, Integer staffId) {
        BaseArea baseArea = areaDao.findFristByQuery(query);
        if (NumberUtil.isNullOrZero(staffId)) {
            return listPageByQuery(query);
        }
        query.setOperatorId(staffId);

        // 根据不同类型调用不同类型数据权限的公司：省
        if (Objects.equals(baseArea.getLevel(), BaseAreaConsts.AREA_LEVEL_TWO)) {
            return listPageByQueryOfPermitProvince(query);
        }

        // 根据不同类型调用不同类型数据权限的公司：市
        if (Objects.equals(baseArea.getLevel(), BaseAreaConsts.AREA_LEVEL_THREE)) {
            return listPageByQueryOfPermitCity(query);
        }

        // 根据不同类型调用不同类型数据权限的公司 ：区
        if (Objects.equals(baseArea.getLevel(), BaseAreaConsts.AREA_LEVEL_FOUR)) {
            SpermitDTO spermitDTO = staffPermitRemoteService.listDpermitByStaffId(staffId, DpermitConsts.TYPE_AREA, true);
            if (spermitDTO.isSuccess() && Objects.equals(spermitDTO.getRange(), SpermitDTO.RANGE_ALL)) {
                return listPageByQuery(query);
            }

            // 权限接口有误，走正常逻辑
            if (!spermitDTO.isSuccess() && Objects.isNull(spermitDTO.getLevel())) {
                return listPageByQuery(query);
            }

            int permitLevel = spermitDTO.getLevel();
            if (Objects.equals(permitLevel, DpermitConsts.TYPE_AREA_COUNTY)) {
                return listPageByQueryOfPermitCounty(query);
            } else {
                return listCountyPageByQueryOfPermitCity(query);
            }
        }

        // 其他公司类型，不支持数据权限
        query.setOperatorId(0);
        return listPageByQuery(query);
    }

    @Override
    public List<BaseArea> listCounty(Integer provinceId, Integer cityId, String countyName) {
        return areaDao.listCounty(provinceId, cityId, countyName);
    }

    @Override
    public List<BaseArea> listStreet(Integer provinceId, Integer cityId, Integer countyId, String streetName) {
        return areaDao.listStreet(provinceId, cityId, countyId, streetName);
    }

    @Override
    public List<BaseArea> listProvinceAndCityByCityNameList(List<String> areaNameList) {
        if (CollectionUtils.isEmpty(areaNameList)) {
            return Collections.emptyList();
        }
        return areaDao.listProvinceAndCityByCityNameList(areaNameList);
    }

    @Override
    public List<DictModel> listAllCity() {
        return areaDao.listAllByLevel(GlobalDict.AREA_LEVEL_CITY);
    }

    @Override
    public List<BaseArea> listAreaByIds(Integer[] ids) {
        return areaDao.listAreaByIds(ids);
    }

    @Override
    @ZmnDPermit(types = {DPermitTypeEnum.COUNTY, DPermitTypeEnum.CITY})
    public List<BaseArea> listByParentIds(Integer[] ids, Integer staffId) {
        if (ids == null || ids.length == 0) {
            return Collections.emptyList();
        }

        return areaDao.listByParentIds(ids, staffId);
    }

    @Override
    public List<AreaDTO> listByChannelId(Integer channelId) {
        return areaDao.listByChannelId(channelId);
    }

    @Override
    public List<AreaSimpleVO> listAreaByChannelId(Integer channelId) {
        return areaDao.listAreaByChannelId(channelId);
    }

    @Override
    public List<AreaSimpleVO> listAllCitySimpleVo() {
        return areaDao.listAllCitySimpleVo();
    }

    @Override
    public List<AreaDTO> listByChannelIdAndServCategId(Integer channelId, Integer showType, Integer servCategId) {
        return areaDao.listByChannelIdAndServCategId(channelId, showType, servCategId);
    }

    @Override
    @Cacheable(cacheNames = "redis1d", key = "'areas:all:municipality'")
    public List<KvModel<Integer, String>> listAllBaseArea() {
        List<KvModel<Integer, String>> items = this.listAllCityAndPro();

        // 直辖市
        KvModel<Integer, String> firstKvModel = new KvModel<>(-1, "直辖市", GlobalConsts.YES);
        List<KvModel> firstKvModelChildren = items.stream().filter(kv -> MUNICIPALITY_AREA_IDS.contains(kv.getKey())).map(kv -> kv.getChildren()).flatMap(Collection::stream)
            .sorted(Comparator.comparing(kv -> (Integer)kv.getKey())).collect(Collectors.toList());
        firstKvModel.setChildren(firstKvModelChildren);

        // 非直辖市
        List<KvModel<Integer, String>> otherKvModelList = items.stream().filter(kv -> {
            boolean flag = !MUNICIPALITY_AREA_IDS.contains(kv.getKey());
            return flag && !CollectionUtils.isEmpty(kv.getChildren());
        }).sorted(Comparator.comparing(item -> InitialUtil.getFristEnName(item.getValue()))).collect(Collectors.toList());

        otherKvModelList.forEach(it -> {
            List<KvModel> children = it.getChildren();
            if (!CollectionUtils.isEmpty(children)) {
                children.sort(Comparator.comparing(kv -> (Integer)kv.getKey()));
            }
        });

        // 分组 排序后的所有城市
        List<KvModel<Integer, String>> baseCities = Lists.newArrayList();
        baseCities.add(firstKvModel);
        baseCities.addAll(otherKvModelList);

        return baseCities;
    }

    @Override
    public List<BaseArea> listAreaByParentIdList(List<Integer> parentIdList, Integer staffId, Integer level) {
        AreaQuery areaQuery = new AreaQuery();
        areaQuery.setLevel(level);
        areaQuery.setParentIdList(parentIdList);
        List<BaseArea> areaList = areaDao.listByQuery(areaQuery);

        if (NumberUtil.isNullOrZero(staffId)) {
            return areaList;
        }
        BaseArea area = areaList.get(0);
        // 根据不同类型调用不同类型数据权限的公司：省 市
        areaList = areaList.stream().filter(e -> area.getLevel().equals(e.getLevel())).collect(Collectors.toList());
        return areaList;
    }

    @Override
    public Integer countByQuery(AreaQuery query) {
        return areaDao.countByQuery(query);
    }

    @Override
    public Integer updateStatusByKey(BaseArea area) {
        return areaDao.updateStatusByKey(area);
    }

    @Override
    public Integer updateFirstLetterByKey(Integer areaId, String firstLetter, String staffName) {
        BaseArea baseArea = new BaseArea();
        baseArea.setAreaId(areaId);
        baseArea.setFirstLetter(firstLetter);
        baseArea.setUpdater(staffName);
        baseArea.setUpdateTime(DateUtil.getNow());
        return areaDao.updateFirstLetterByKey(baseArea);
    }

    @Override
    public Integer updateSimpleSpellByKey(Integer areaId, String simpleSpell, String staffName) {
        BaseArea baseArea = new BaseArea();
        baseArea.setAreaId(areaId);
        baseArea.setSimpleSpell(simpleSpell);
        baseArea.setUpdater(staffName);
        baseArea.setUpdateTime(DateUtil.getNow());
        return areaDao.updateSimpleSpellByKey(baseArea);
    }

    @Override
    public Integer updateStatusBatchByKey(List<Integer> ids, Integer status, String mcStaffName) {
        return areaDao.updateBatchStatusByKey(ids, status, mcStaffName);
    }

    @Override
    public Integer updateFirstLetterAndSimpleSpellByKeyBatch(List<BaseArea> areaList) {
        return areaDao.updateFirstLetterAndSimpleSpellByKeyBatch(areaList);
    }

    @Override
    public Integer updateSortByKey(BaseArea area) {
        return areaDao.updateSortByKey(area);
    }

    @Override
    public Integer updateSortByKey(Integer areaId, Integer sort, String mcStaffName) {
        BaseArea area = new BaseArea();
        area.setAreaId(areaId);
        area.setSort(sort);
        area.setUpdater(mcStaffName);

        return updateSortByKey(area);
    }

    @Override
    @CacheEvict(cacheNames = "redis1d", key = "'area:id:'+#area.areaId")
    public Integer updateByKey(BaseArea area) {
        return areaDao.updateByKey(area);
    }

    @Override
    public Integer updateCodeByKey(BaseArea area) {
        return areaDao.updateCodeByKey(area);
    }

    @Override
    @CacheEvict(cacheNames = "redis1d", key = "'areas:level:'+#area.level")
    public Integer insert(BaseArea area) {
        return areaDao.insert(area);
    }

    @Override
    public Integer deleteByKey(Integer areaId) {
        return areaDao.deleteByKey(areaId);
    }

    /**
     * 根据百度地图返回的 adcode 映射BaseArea
     * <p>
     * adcode有可能是区县和城市，需要判断处理 adcode有可能和zmn的code对应不上，但有可能和id对应上，需要处理
     *
     * @param addressComponent
     * @return
     */
    private BaseArea getBaseAreaByLocation(AddressComponent addressComponent) {
        // 根据Code查询是否存在区域
        BaseArea baseArea = areaCache.getAreaByCode(addressComponent.getAdcode());

        // 根据ID查询是否存在区域
        if (baseArea == null || NumberUtil.isNullOrZero(baseArea.getAreaId())) {
            baseArea = areaDao.findByKey(addressComponent.getAdcode());
        }

        // 根据名称查询区域
        AreaQuery query;
        List<BaseArea> baseAreaList;
        if (baseArea == null) {
            // 检查省
            String province = addressComponent.getProvince();
            query = new AreaQuery();
            query.setName(province);
            query.setLevel(GlobalConsts.AREA_PROVINCE);
            baseAreaList = areaDao.listByQuery(query);
            if (CollectionUtils.isEmpty(baseAreaList)) {
                return null;
            }

            // 检查市
            String city = addressComponent.getCity();
            query = new AreaQuery();
            query.setName(city);
            query.setParentId(baseAreaList.get(0).getAreaId());
            query.setLevel(GlobalConsts.AREA_CITY);
            baseAreaList = areaDao.listByQuery(query);
            if (CollectionUtils.isEmpty(baseAreaList)) {
                return null;
            }
            baseArea = baseAreaList.get(0);
        }

        if (baseArea == null || NumberUtil.isNullOrZero(baseArea.getAreaId())) {
            return null;
        }

        // 如果百度映射回来的是市级，则根据区名称模糊匹配4级区，如果区名称也为空，则随便取市下一个区返回
        if (baseArea.getLevel() == GlobalConsts.AREA_CITY) {
            String county = addressComponent.getDistrict();
            if (!StringUtils.isEmpty(county)) {
                query = new AreaQuery();
                query.setAlias(county);
                query.setParentId(baseArea.getAreaId());
                query.setLevel(GlobalConsts.AREA_COUNTY);
                baseAreaList = areaDao.listByQuery(query);
            } else {
                baseAreaList = areaCache.listAreaByParentId(baseArea.getAreaId());
            }

            if (!CollectionUtils.isEmpty(baseAreaList)) {
                return baseAreaList.get(0);
            }
        }

        if (baseArea.getLevel() == GlobalConsts.AREA_COUNTY) {
            return baseArea;
        }

        return null;
    }
}
